package com.test.aop;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * AdvisedSupport类提供了对代理支持的相关操作，包括管理通知器、顾问以及目标源等
 */
public class AdvisedSupport {

    // 冻结状态，一旦设置为true，将无法添加新的顾问
    private boolean frozen = false;

    // 目标源，包含目标类的信息
    private TargetSource targetSource;

    // 保存所有的顾问
    private List<Advisor> advisors = new ArrayList<>();

    // 方法缓存，用于缓存方法和其对应的拦截器链
    private transient Map<Method, List<Object>> methodCache = new ConcurrentHashMap<>(32);

    /**
     * 设置目标源
     * @param targetSource 目标源对象
     */
    public void setTargetSource(TargetSource targetSource) {
        this.targetSource = targetSource;
    }

    /**
     * 获取目标源
     * @return 目标源对象
     */
    public TargetSource getTargetSource() {
        return this.targetSource;
    }

    /**
     * 设置冻结状态
     * @param frozen 冻结状态，true表示冻结，false表示未冻结
     */
    public void setFrozen (boolean frozen) {
        this.frozen = frozen;
    }

    /**
     * 检查是否处于冻结状态
     * @return 如果处于冻结状态返回true，否则返回false
     */
    public boolean isFrozen() {
        return this.frozen;
    }

    /**
     * 添加顾问到advisors列表
     * 如果当前状态为冻结，将抛出异常
     * @param advisor 要添加的顾问对象
     * @throws UnsupportedOperationException 如果当前状态为冻结，将抛出此异常
     */
    public void addAdvisors(Advisor advisor) {
        if (isFrozen()){
            throw new UnsupportedOperationException("无法添加新顾问：AdvisedSupport 已冻结");
        }
        this.advisors.add(advisor);
        this.methodCache.clear();
    }

    /**
     * 获取所有的顾问
     * @return 包含所有顾问的列表
     */
    public List<Advisor> getAdvisors() {
        return this.advisors;
    }

    /**
     * 获取目标类
     * @return 目标类的Class对象
     */
    public Class<?> getTargetClass() {
        return getTargetSource().getTargetClass();
    }

    /**
     * 检查给定方法和目标类是否有建议
     * @param method 方法对象
     * @param targetClass 目标类的Class对象
     * @return 如果有建议返回true，否则返回false
     */
    public boolean hasAdvice(Method method, Class<?> targetClass){
        return !getInterceptorsAndDynamicInterceptionAdvice(method, targetClass).isEmpty();
    }

    /**
     * 获取给定方法和目标类的拦截器链和动态拦截建议
     * @param method 方法对象
     * @param targetClass 目标类的Class对象
     * @return 包含拦截器和动态拦截建议的列表
     */
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
        List<Object> cached = this.methodCache.get(method);
        if (cached != null) {
            return cached;
        }
        System.out.println("AdvisedSupport: 计算方法 " + method.getName() + " 的拦截器链");
        System.out.println("AdvisedSupport: 获取目标类 " + targetClass.getName() + " 的所有顾问");
        System.out.println("AdvisedSupport: 接口 " + (targetClass.getInterfaces().length > 0 ? targetClass.getInterfaces()[0].getName() : "无"));
        System.out.println("AdvisedSupport: 通知器数量 " + this.advisors.size());

        //计算适用于此方法的通知
        List<Object> interceptors = new ArrayList<>();
        for (Advisor advisor : this.advisors) {
            if (advisor instanceof PointcutAdvisor){
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                boolean classMatches = pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass);

                if (!classMatches && targetClass.getInterfaces().length > 0){
                    for (Class<?> targetInterface : targetClass.getInterfaces()) {
                        if (pointcutAdvisor.getPointcut().getClassFilter().matches(targetInterface)){
                            classMatches = true;
                            break;
                        }
                    }
                }

                boolean methodMatches = false;
                if (classMatches){
                    methodMatches = pointcutAdvisor.getPointcut().getMethodMatcher().matches(method, targetClass);

                    if (!methodMatches && targetClass.getInterfaces().length > 0){
                        for (Class<?> iface : targetClass.getInterfaces()){
                            try {
                                Method ifaceMethod = iface.getMethod(method.getName(), method.getParameterTypes());
                                methodMatches = pointcutAdvisor.getPointcut().getMethodMatcher().matches(method, iface);
                                if (methodMatches)
                                    break;
                            } catch (NoSuchMethodException e) {
                                System.out.println("AdvisedSupport: 接口" + iface.getName() + " 没有方法 " + method.getName());
                            }
                        }
                    }
                }
                if (classMatches && methodMatches){
                    interceptors.add(advisor.getAdvice());
                }else {
                    interceptors.add(advisor.getAdvice());
                }
            }
        }

        this.methodCache.put(method, interceptors);
        return interceptors;
    }
}
